package com.chr.controller;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.MessageDigest;
import java.util.*;

import com.chr.constant.Constant;
import com.chr.entity.User;
import com.chr.service.UserService;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.chr.entity.Order;
import com.chr.properties.WeixinpayProperties;
import com.chr.service.OrderService;
import com.chr.util.DateUtil;
import com.chr.util.HttpClientUtil;
import com.chr.util.MD5Util;
import com.chr.util.StringUtil;
import com.chr.util.XmlUtil;

/**
 * 微信支付Controller
 * @author Administrator
 *
 */
@Controller
@RequestMapping("/weixinpay")
public class WeixinpayController {

	@Resource
	private WeixinpayProperties weixinpayProperties;
	
	@Resource
	private OrderService orderService;

	@Autowired
	private UserService userService;

	private final static Logger logger= LoggerFactory.getLogger(WeixinpayController.class);
	
	/**
	 * 微信请求
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping("/pay")
	public ModelAndView pay(HttpServletRequest request,HttpServletResponse response,HttpSession session)throws Exception {
		User currentUser = (User) session.getAttribute("currentUser");


		// 判断用户是否登录
		if (currentUser == null) {
			response.setContentType("text/html; charset=UTF-8");//注意text/html，和application/html
			response.getWriter().print("<html><body><script type='text/javascript'>alert('大佬，请您先登录下哈！');history.back(-1);</script></body></html>");
			response.getWriter().close();
		} else {
			logger.info(currentUser.getOpenid() + " 用户发起支付请求，IP:" + request.getRemoteHost());
			ServletContext application = request.getServletContext();
			Map<String, String> propertyMap = (HashMap) application.getAttribute("propertyMap"); //  支付总金额
			String totalAmount = propertyMap.get("k10");
			Order order = new Order();
			String orderNo = DateUtil.getCurrentDateStr(); // 生成订单号
			String subject = "Java1234-VIP会员购买"; // 订单名称

			order.setSubject(subject);
			order.setTotalAmount(totalAmount);
			// order.setTotalAmount("1");
			order.setPay(false);
			order.setOrderNo(orderNo);
			order.setWay("微信");
			order.setUser(currentUser);

			orderService.add(order);

			ModelAndView mav = new ModelAndView();
			mav.addObject("order", order);
			mav.addObject("title", "微信扫码在线支付");
			mav.setViewName("weixinpay");
			return mav;


		}
		return null;
	}


	
	/**
	 * 微信支付服务器异步通知
	 * @param request
	 * @throws Exception
	 */
	@RequestMapping("/notifyUrl")
	public void notifyUrl(HttpServletRequest request)throws Exception{
		logger.info("notifyUrl");
		//读取参数    
        InputStream inputStream ;    
        StringBuffer sb = new StringBuffer();    
        inputStream = request.getInputStream();    
        String s ;    
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));    
        while ((s = in.readLine()) != null){    
            sb.append(s);    
        }    
        in.close();    
        inputStream.close();
		logger.info("sb:"+sb.toString());
        //解析xml成map    
        Map<String, String> m = new HashMap<String, String>();    
        m = XmlUtil.doXMLParse(sb.toString());    
            
        //过滤空 设置 TreeMap    
        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();          
        Iterator<String> it = m.keySet().iterator();    
        while (it.hasNext()) {    
            String parameter = it.next();    
            String parameterValue = m.get(parameter);    
                
            String v = "";    
            if(null != parameterValue) {    
                v = parameterValue.trim();    
            } 
            logger.info("p:"+parameter+",v:"+v);
            packageParams.put(parameter, v);    
        }  
        
        // 微信支付的API密钥    
        String key = weixinpayProperties.getKey();
		String out_trade_no=(String) packageParams.get("out_trade_no");
        if(isTenpaySign("UTF-8", packageParams, key)){ // 验证通过
        	if("SUCCESS".equals((String)packageParams.get("result_code"))){  

        		Order order=orderService.findById(out_trade_no);
        		if(order!=null){
					order.setBuyTime(new Date()); // 支付时间
					order.setPay(true); // 设置支付状态已经支付
					orderService.update(order);
					logger.info(out_trade_no+"：微信服务器异步修改订单状态成功！");

					// 修改用户会员等级
					User user=order.getUser();
					user.setLevel(Constant.LEVEL_VIP);
					userService.update(user);

					logger.info(out_trade_no+"：微信服务器异步修改会员等级成功！");
				}
        	}
        }else{
			logger.info(out_trade_no+"：微信服务器异步验证失败！");
        }
	}
	
	/**
	 * 加载支付二维码
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping("/loadPayImage")
	public void loadPayImage(HttpServletRequest request,HttpServletResponse response)throws Exception{
		String id=request.getParameter("id");
		Order order=orderService.findById(id);
		String orderNo=DateUtil.getCurrentDateStr(); // 生成订单号
		Map<String,Object> map=new HashMap<String,Object>();
		map.put("appid", weixinpayProperties.getAppid());
		map.put("mch_id", weixinpayProperties.getMch_id());
		map.put("device_info", weixinpayProperties.getDevice_info());
		map.put("notify_url", weixinpayProperties.getNotify_url());
		map.put("trade_type", "NATIVE"); // 交易类型
		map.put("out_trade_no", orderNo);
		map.put("body", "Java1234-VIP购买");
		map.put("total_fee", Integer.parseInt(order.getTotalAmount())*100);
		map.put("nonce_str", StringUtil.getRandomString(30)); // 随机串
		// map.put("spbill_create_ip", getRemortIP(request)); // 终端IP
		map.put("spbill_create_ip", "127.0.0.1"); // 终端IP
		map.put("sign", getSign(map)); // 签名
		String xml=XmlUtil.genXml(map);
		System.out.println(xml);
		
		InputStream in = HttpClientUtil.sendXMLDataByPost(weixinpayProperties.getUrl().toString(),xml).getEntity().getContent(); // 发送xml消息
		String code_url=getElementValue(in,"code_url"); 
		MultiFormatWriter multiFormatWriter = new MultiFormatWriter();  
	     Map hints = new HashMap();  
	     BitMatrix bitMatrix = null;  
	     try {  
	         bitMatrix = multiFormatWriter.encode(code_url, BarcodeFormat.QR_CODE, 250, 250,hints);  
	         BufferedImage image = toBufferedImage(bitMatrix);  
	         //输出二维码图片流  
	         try {  
	             ImageIO.write(image, "png", response.getOutputStream());  
	         } catch (IOException e) {  
	             e.printStackTrace();  
	         }finally{
	     		order.setOrderNo(orderNo); // 设置订单号
	     		orderService.update(order); // 保存订单信息
	         }
	     } catch (WriterException e1) {  
	         e1.printStackTrace();  
	     }           
	}
	
	/**
	 * 查询订单支付状态
	 * @param id
	 * @return
	 * @throws Exception
	 */
	@ResponseBody
	@RequestMapping("/loadPayState")
	public Boolean loadPayState(String id)throws Exception{
		Order order=orderService.findById(id);
		return order.isPay();
	}
	
	/**
	 * 微信支付同步通知页面
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("/returnUrl")
	public ModelAndView returnUrl()throws Exception{
		logger.info("微信服务器同步通知进入");
		ModelAndView mav=new ModelAndView();
		mav.addObject("title", "同步通知地址_Java知识分享网");
		mav.addObject("success", true);
		mav.setViewName("returnUrl");
		return mav;
	}
	
	/** 
	   * 类型转换 
	   * @author chenp 
	   * @param matrix 
	   * @return 
	   */  
	 public static BufferedImage toBufferedImage(BitMatrix matrix) {  
      int width = matrix.getWidth();  
      int height = matrix.getHeight();  
      BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);  
      for (int x = 0; x < width; x++) {  
          for (int y = 0; y < height; y++) {  
              image.setRGB(x, y, matrix.get(x, y) == true ? 0xff000000 : 0xFFFFFFFF);  
          }  
      }  
      return image;  
	 }  
	
	/**
	 * 通过返回IO流获取支付地址
	 * @param in
	 * @return
	 */
	private String getElementValue(InputStream in,String key)throws Exception{
		SAXReader reader = new SAXReader();
    Document document = reader.read(in);
    Element root = document.getRootElement();
    List<Element> childElements = root.elements();
    for (Element child : childElements) {
    	if(key.equals(child.getName())){
    		return child.getStringValue();
    	}
    }
    return null;
	}
	
	/**
 * 获取本机IP地址
 * @return IP
 */
	public static String getRemortIP(HttpServletRequest request) {  
    if (request.getHeader("x-forwarded-for") == null) {  
        return request.getRemoteAddr();  
    }  
    return request.getHeader("x-forwarded-for");  
} 

/**
 * 微信支付签名算法sign
 */
private String getSign(Map<String,Object> map) {
    StringBuffer sb = new StringBuffer();
    String[] keyArr = (String[]) map.keySet().toArray(new String[map.keySet().size()]);//获取map中的key转为array
    Arrays.sort(keyArr);//对array排序
    for (int i = 0, size = keyArr.length; i < size; ++i) {
        if ("sign".equals(keyArr[i])) {
            continue;
        }
        sb.append(keyArr[i] + "=" + map.get(keyArr[i]) + "&");
    }
    sb.append("key=" + weixinpayProperties.getKey());
    String sign = string2MD5(sb.toString());
    return sign;
}

/***
 * MD5加码 生成32位md5码
 */
private String string2MD5(String str){
    if (str == null || str.length() == 0) {
        return null;
    }
    char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f' };

    try {
        MessageDigest mdTemp = MessageDigest.getInstance("MD5");
        mdTemp.update(str.getBytes("UTF-8"));

        byte[] md = mdTemp.digest();
        int j = md.length;
        char buf[] = new char[j * 2];
        int k = 0;
        for (int i = 0; i < j; i++) {
            byte byte0 = md[i];
            buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
            buf[k++] = hexDigits[byte0 & 0xf];
        }
        return new String(buf).toUpperCase();
    } catch (Exception e) {
        return null;
    }
}


/**  
 * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。  
 * @return boolean  
 */    
public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {    
    StringBuffer sb = new StringBuffer();    
    Set es = packageParams.entrySet();    
    Iterator it = es.iterator();    
    while(it.hasNext()) {    
        Map.Entry entry = (Map.Entry)it.next();    
        String k = (String)entry.getKey();    
        String v = (String)entry.getValue();    
        if(!"sign".equals(k) && null != v && !"".equals(v)) {    
            sb.append(k + "=" + v + "&");    
        }    
    }    
    sb.append("key=" + API_KEY);    
        
    //算出摘要    
    String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();    
    String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();    
        
    return tenpaySign.equals(mysign);    
}  
}
